package com.medusa.aps.business.modules.security.mp.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.medusa.aps.business.model.pagination.Page;
import com.medusa.aps.business.modules.security.model.input.MenuQueryPara;
import com.medusa.aps.business.modules.security.mp.entity.Menu;
import com.medusa.aps.business.modules.security.mp.mapper.MenuMapper;
import com.medusa.aps.business.modules.security.mp.service.IMenuService;
import com.medusa.aps.business.modules.security.mp.service.IRoleMenuService;
import com.medusa.aps.business.modules.security.mp.service.IUserRoleService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xianhui.yu
 **/
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private IUserRoleService userRoleService;

    @Resource
    private IRoleMenuService roleMenuService;

    @Override
    public void listPage(Page<Menu> page, MenuQueryPara filter) {
        page.setRecords(menuMapper.selectMenus(page, filter));
    }


    public Page<Menu> pageMenu(MenuQueryPara menuQueryPara) {
        boolean hasText = StringUtils.hasText(menuQueryPara.getTitle());
        boolean stateNotnull = menuQueryPara.getState() != null;
        LambdaQueryWrapper<Menu> queryWrapper = new LambdaQueryWrapper<Menu>()
                .like(hasText, Menu::getTitle, menuQueryPara.getTitle())
                .eq(stateNotnull, Menu::getState, menuQueryPara.getState())
                .eq(!(hasText || stateNotnull), Menu::getParentId, 0);
        Page<Menu> menuPage = menuMapper.selectPage(menuQueryPara, queryWrapper);
        List<Menu> menus = menuPage.getRecords();
        if (CollectionUtils.isEmpty(menus)) {
            return menuPage;
        }
        if (hasText || stateNotnull) {
            long size = menuQueryPara.getSize();
            menuQueryPara.setSize(menuPage.getTotal());
            menuPage = menuMapper.selectPage(menuQueryPara, queryWrapper);
            menuQueryPara.setSize(size);
            menus = menuPage.getRecords();
            List<Menu> rootMenu = new ArrayList<>();
            List<Menu> menuChildren = new ArrayList<>();
            for (Menu menu : menus) {
                if (menu.getParentId() == 0) {
                    rootMenu.add(menu);
                } else {
                    menuChildren.add(menu);
                }
            }
            if (CollectionUtils.isEmpty(menuChildren)) {
                // 找到所有子级菜单
                return findChildren(menuPage, menus, menus.stream().map(Menu::getId).toList());
            }
            List<Long> menuIds = menuChildren.stream().map(Menu::getId).toList();
            // 找到所有父级菜单
            List<Menu> parentMenus = menuMapper.findParentMenus(menuIds);
            if (CollUtil.isNotEmpty(rootMenu)) {
                // 将单个没有子级的父菜单添加进去
                List<Long> filterParentId = parentMenus.stream().filter(menu -> menu.getParentId() == 0).map(Menu::getId).toList();
                for (Menu menu : rootMenu) {
                    if (!filterParentId.contains(menu.getId())) {
                        parentMenus.add(menu);
                    }
                }
            }
            menus = handleMenus(parentMenus);
            int total = menus.size();
            menuPage.setRecords(menus.subList((int) menuPage.getCurrent() - 1, menuPage.getSize() > total ? total : (int) menuPage.getSize()));
            menuPage.setTotal(total);
            menuPage.setPages((total + size - 1) / size);
            return menuPage;
        }
        List<Long> parentIds = menus.stream().map(Menu::getId).toList();
        // 找到所有子级菜单
        return findChildren(menuPage, menus, parentIds);

    }

    private Page<Menu> findChildren(Page<Menu> menuPage, List<Menu> menus, List<Long> parentIds) {
        List<Menu> childrenMenus = menuMapper.findChildrenMenus(parentIds);
        if (CollectionUtils.isEmpty(childrenMenus)) {
            return menuPage;
        }
        childrenMenus.addAll(menus);
        menuPage.setRecords(handleMenus(childrenMenus));
        return menuPage;
    }


    private List<Menu> handleMenus(List<Menu> allMenus) {
        return allMenus.stream()
                .filter(menu -> menu.getParentId() == 0)
                .peek(root -> root.setMenuChildren(getChildren(root, allMenus)))
                .sorted(Comparator.comparing(menu -> menu.getSortNo() == null ? 0 : menu.getSortNo()))
                .collect(Collectors.toList());
    }

    @Override
    public List<Menu> treeMenu() {
        List<Menu> menus = menuMapper.selectList(null);
        if (CollectionUtils.isEmpty(menus)) {
            return Collections.emptyList();
        }
        return handleMenus(menus);

    }

    @Override
    public Set<String> menuNames(Set<Long> menuIds) {
        Map<Long, List<Menu>> parentIdMap = this.lambdaQuery()
                .select(Menu::getId, Menu::getParentId, Menu::getTitle)
                .in(Menu::getId, menuIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(Menu::getParentId));
        Map<Long, String> menuNameMap = new HashMap<>() {
            {
                put(0L, "/顶级");
            }
        };
        Set<String> menuNames = new HashSet<>();
        List<Menu> menus = parentIdMap.get(0L);
        while (CollUtil.isNotEmpty(menus)) {
            List<Menu> children = new ArrayList<>();
            for (Menu menu : menus) {
                String parentName = menuNameMap.get(menu.getParentId());
                String nodeName = parentName + StrPool.SLASH + menu.getTitle();
                Long menuId = menu.getId();
                menuNameMap.put(menuId, nodeName);
                List<Menu> currChildren = parentIdMap.get(menuId);
                if (CollUtil.isNotEmpty(currChildren)) {
                    children.addAll(currChildren);
                    continue;
                }
                menuNames.add(nodeName);
            }
            menus = children;
        }
        return menuNames;

    }

    private List<Menu> getChildren(Menu root, List<Menu> menus) {
        return menus.stream()
                .filter(menu -> root.getId().equals(menu.getParentId()))
                .map(child -> child.setMenuChildren(getChildren(child, menus)))
                .sorted(Comparator.comparing(menu -> menu.getSortNo() == null ? 0 : menu.getSortNo()))
                .toList();

    }
}
